package object

import (
	"bytes"
	"fmt"
	"hash/fnv"
	"strings"

	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/ast"
	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/code"
)

const (
	INTEGER_OBJ           = "INTEGER"
	BOOLEAN_OBJ           = "BOOLEAN"
	NULL_OBJ              = "NULL"
	RETURN_VALUE_OBJ      = "RETURN_VALUE"
	ERROR_OBJ             = "ERROR"
	FUNCTION_OBJ          = "FUNCTION"
	STRING_OBJ            = "STRING"
	BUILTIN_OBJ           = "BUILTIN"
	ARRAY_OBJ             = "ARRAY"
	HASH_OBJ              = "HASH"
	COMPILED_FUNCTION_OBJ = "COMPILED_FUNCTION_OBJ"
)

// 定义对象接口，要求实现 Type()和Inspect()方法
type ObjectType string
type Object interface {
	Type() ObjectType
	Inspect() string
}

// 定义hashxable接口，要求所有可以做哈希字典的键的类型必须实现该接口
// 该接口要求实现一个 HashKey方法返回HashKey类型
// 目前来看 整数和字符串是有必要实现该接口的
type Hashable interface {
	HashKey() HashKey
}

// HashKey类型主要是存储目标键的哈希值以及目标键的类型
type HashKey struct {
	Type  ObjectType
	Value uint64
}

// - 整数
type Integer struct {
	Value int64
}

func (i *Integer) Inspect() string  { return fmt.Sprintf("%d", i.Value) }
func (i *Integer) Type() ObjectType { return INTEGER_OBJ }
func (i *Integer) HashKey() HashKey {
	return HashKey{
		Type:  INTEGER_OBJ,
		Value: uint64(i.Value),
	}
}

// - 布尔值
type Boolean struct {
	Value bool
}

func (b *Boolean) Inspect() string  { return fmt.Sprintf("%t", b.Value) }
func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ }

// - null
type Null struct{}

func (n *Null) Inspect() string  { return "null" }
func (n *Null) Type() ObjectType { return NULL_OBJ }

// - return语句的结果表达对象
// -- 之所以把return语句的结果封装为对象是因为return即可以在if的block之中，也可能在program的语句列表之中
// -- 如果是block之中的return，可能会存在多条return的情况，如果我们根据上一条语句的执行结果是否是return语句的结果来判断语句执行结果就可以提前终止后续的解析。
// --- 我自己最初的版本是判断上一条语句是否是return语句，但是其实在两条return语句的时候，第一条很可能是包含在if语句中的，所以根本无法阻挡第二条return语句的被解析。
// --- 而如果我们依赖解析结果是否是return对象(相当于携带了上下文)，就可以轻松破解这个问题
// ---- 不得不说 waiig 的作者的代码能力实在是强!!!
type ReturnValue struct {
	Value Object
}

func (rv *ReturnValue) Inspect() string  { return rv.Value.Inspect() }
func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }

// - error
// -- 错误对象，在出现逻辑错误的时候响应给调用方
type Error struct {
	Message string
}

func (e *Error) Inspect() string  { return "ERROR: " + e.Message }
func (e *Error) Type() ObjectType { return ERROR_OBJ }

// - function
type Function struct {
	Parameters []*ast.Identifier
	Body       *ast.BlockStatement
	Env        *Environment
}

func (f *Function) Type() ObjectType { return FUNCTION_OBJ }
func (f *Function) Inspect() string {
	var out bytes.Buffer
	params := []string{}
	for _, p := range f.Parameters {
		params = append(params, p.String())
	}

	out.WriteString("fn")
	out.WriteString("(")
	out.WriteString(strings.Join(params, ", "))
	out.WriteString(") {\n")
	out.WriteString(f.Body.String())
	out.WriteString("\n}")

	return out.String()
}

// -- 内置函数 len
var BuiltinFunctionList = []string{
	"len",
}

// - string
type String struct {
	Value string
}

func (s *String) Inspect() string  { return s.Value }
func (s *String) Type() ObjectType { return STRING_OBJ }

// 字符串实现哈希值
// 使用 FNV 这个哈希算法
func (s *String) HashKey() HashKey {
	h := fnv.New64a()
	h.Write([]byte(s.Value))
	return HashKey{
		Type:  s.Type(),
		Value: h.Sum64(),
	}
}

// - 内置函数 对象
// 因为我们的内置函数对象是用来处理各种对象的输入，并输出某种对象，所以定义一个统一的方法模板
type BuiltinFunction func(args ...Object) (Object, error)
type Builtin struct {
	Fn BuiltinFunction
}

func (b *Builtin) Inspect() string  { return "builtin function" }
func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ }

// - array 数组
type Array struct {
	Elements []Object
}

func (a *Array) Type() ObjectType { return ARRAY_OBJ }
func (a *Array) Inspect() string {
	var out bytes.Buffer
	params := []string{}
	for _, p := range a.Elements {
		params = append(params, p.Inspect())
	}

	out.WriteString("[")
	out.WriteString(strings.Join(params, ", "))
	out.WriteString("]")

	return out.String()
}

// - hash 哈希字典
type Hash struct {
	Pairs map[HashKey]HashPair
}
type HashPair struct {
	Key   Object
	Value Object
}

func (h *Hash) Type() ObjectType { return HASH_OBJ }
func (h *Hash) Inspect() string {
	var out bytes.Buffer

	pairs := []string{}
	for _, pair := range h.Pairs {
		pairs = append(pairs, fmt.Sprintf("%s: %s", pair.Key.Inspect(), pair.Value.Inspect()))
	}

	out.WriteString("{")
	out.WriteString(strings.Join(pairs, ", "))
	out.WriteString("}")

	return out.String()
}

// !+ function
// 函数作为一个整体，在编译的时候作为一个object放到常量池中
type CompiledFunction struct {
	Instructions code.Instructions
}

func (cf *CompiledFunction) Type() ObjectType { return COMPILED_FUNCTION_OBJ }
func (cf *CompiledFunction) Inspect() string {
	// %p 占位符主要用于输出变量的内存地址，也就是变量在内存中存储位置的起始地址。
	return fmt.Sprintf("CompiledFunction[%p]", cf)
}

// !- function

// ------------------- 绑定关系 -----------------------
// 将值对象和标识符绑定到一起
type Environment struct {
	store map[string]Object // 其实string也可以用标识符对象代替！！
	Outer *Environment
}

func NewEnvironment() *Environment {
	s := make(map[string]Object)
	newEnv := &Environment{store: s}
	return newEnv
}

func (e *Environment) Get(name string) (Object, bool) {
	obj, ok := e.store[name]
	if !ok && e.Outer != nil {
		obj, ok = e.Outer.store[name]
	}
	return obj, ok
}
func (e *Environment) Set(name string, val Object) Object {
	e.store[name] = val
	return val
}

// 定义一个新的环境变量
// 该环境变量携带者包裹自己的环境变量
// 该种环境变量常常用来函数的嵌套之中
func NewEncloseEnvironment(outer *Environment) *Environment {
	env := NewEnvironment()
	env.Outer = outer
	return env
}
